package imp

import (
	sqlc "bgs/db/sqlc"
	productGrpc "bgs/grpc/gen/service/product"
	"bgs/grpc/gen/shared/message"
	"bgs/service/model"
	productService "bgs/service/product"
	"bgs/util"
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
)

/****************************************************************************************/
/*                               product entity of helper                               */
/****************************************************************************************/

func transformProductEntityToPb(productAttributeKeys []sqlc.ProductAttributeKey, productAttributeValues []sqlc.ProductAttributeValue, productSpecs []sqlc.ProductSpec) func(sqlc.Product) *productGrpc.ProductEntity {
	return func(o sqlc.Product) *productGrpc.ProductEntity {
		v := &productGrpc.ProductEntity{
			Id:             o.ID,
			Origin:         o.Origin,
			BrandId:        o.BrandID,
			Name:           o.Name,
			Pics:           o.Pics,
			Intro:          o.Intro.String,
			IntroPics:      o.IntroPics,
			HasSpecs:       o.HasSpecs,
			PriceMin:       o.PriceMin,
			PriceMax:       o.PriceMax,
			ProductGroupId: o.ProductGroupID.Int64,
			// RemotePrdTypeId:      o.RemotePrdTypeID,
			RemotePk:             o.RemotePk,
			ProductAttributeDefs: mapProductAttributeDefEntityToPb(productAttributeKeys, productAttributeValues, transProductAttributeKeyValuesToPb),
			ProductSpecs:         mapProductSpecEntityToPb(productSpecs, transformProductSpecEntityToPb),
		}

		// 兼容上游系统无规格也插入规格记录
		v.HasSpecs = len(v.ProductAttributeDefs) > 0
		return v
	}
}

func mapProductEntityToPb(list []sqlc.Product, f func(sqlc.Product) *productGrpc.ProductEntity) []*productGrpc.ProductEntity {
	r := make([]*productGrpc.ProductEntity, len(list))
	for i, item := range list {
		r[i] = f(item)
	}
	return r
}

func mapGenericToProductAttributeKey(list []interface{}, f func(interface{}) sqlc.ProductAttributeKey) []sqlc.ProductAttributeKey {
	r := make([]sqlc.ProductAttributeKey, len(list))
	for i, item := range list {
		r[i] = f(item)
	}
	return r
}

func mapGenericToProductAttributeValue(list []interface{}, f func(interface{}) sqlc.ProductAttributeValue) []sqlc.ProductAttributeValue {
	r := make([]sqlc.ProductAttributeValue, len(list))
	for i, item := range list {
		r[i] = f(item)
	}
	return r
}

func mapGenericToProductSpec(list []interface{}, f func(interface{}) sqlc.ProductSpec) []sqlc.ProductSpec {
	r := make([]sqlc.ProductSpec, len(list))
	for i, item := range list {
		r[i] = f(item)
	}
	return r
}

func transformProductEntityFilteredToPb(productAttributeKeys []sqlc.ProductAttributeKey, productAttributeValues []sqlc.ProductAttributeValue, productSpecs []sqlc.ProductSpec) func(sqlc.Product) *productGrpc.ProductEntity {
	return func(o sqlc.Product) *productGrpc.ProductEntity {
		filteredProductAttributeKeys := mapGenericToProductAttributeKey(util.Filter(util.ToGenericSlice(productAttributeKeys), func(v interface{}) bool {
			pav := v.(sqlc.ProductAttributeKey)
			return pav.ProductID == o.ID
		}), func(v interface{}) sqlc.ProductAttributeKey {
			return v.(sqlc.ProductAttributeKey)
		})

		filteredProductAttributeValues := mapGenericToProductAttributeValue(util.Filter(util.ToGenericSlice(productAttributeValues), func(v interface{}) bool {
			pav := v.(sqlc.ProductAttributeValue)
			return pav.ProductID == o.ID
		}), func(v interface{}) sqlc.ProductAttributeValue {
			return v.(sqlc.ProductAttributeValue)
		})

		filteredProductAttributeSpecs := mapGenericToProductSpec(util.Filter(util.ToGenericSlice(productSpecs), func(v interface{}) bool {
			pav := v.(sqlc.ProductSpec)
			return pav.ProductID == o.ID
		}), func(v interface{}) sqlc.ProductSpec {
			return v.(sqlc.ProductSpec)
		})

		return transformProductEntityToPb(filteredProductAttributeKeys, filteredProductAttributeValues, filteredProductAttributeSpecs)(o)
	}
}

func transProductAttributeKeyValuesToPb(productAttributeKey sqlc.ProductAttributeKey, productAttributeValues []sqlc.ProductAttributeValue) *productGrpc.ProductAttributeDefEntity {

	productAttributeValuesOfTheKey := util.Filter(util.ToGenericSlice(productAttributeValues), func(v interface{}) bool {
		pav := v.(sqlc.ProductAttributeValue)
		return pav.ProductAttributeKeyID == productAttributeKey.ID
	})

	return &productGrpc.ProductAttributeDefEntity{
		Key: productAttributeKey.Name,
		Values: util.MapToString(productAttributeValuesOfTheKey, func(v interface{}) string {
			pav := v.(sqlc.ProductAttributeValue)
			return pav.Value
		}),
		// RemotePk: productAttributeKey.RemotePk,
	}
}

func mapProductAttributeDefEntityToPb(keys []sqlc.ProductAttributeKey, values []sqlc.ProductAttributeValue, f func(sqlc.ProductAttributeKey, []sqlc.ProductAttributeValue) *productGrpc.ProductAttributeDefEntity) []*productGrpc.ProductAttributeDefEntity {
	r := make([]*productGrpc.ProductAttributeDefEntity, len(keys))
	for i, item := range keys {
		r[i] = f(item, values)
	}
	return r
}

func transformProductSpecEntityToPb(productSpec sqlc.ProductSpec) *productGrpc.ProductSpecEntity {
	return &productGrpc.ProductSpecEntity{
		Id:                productSpec.ID,
		Name:              productSpec.Name,
		ProductAttributes: string(productSpec.ProductAttributes),
		Price:             productSpec.Price,
		// RemotePk:          productSpec.RemotePk,
	}
}

func mapProductSpecEntityToPb(list []sqlc.ProductSpec, f func(sqlc.ProductSpec) *productGrpc.ProductSpecEntity) []*productGrpc.ProductSpecEntity {
	r := make([]*productGrpc.ProductSpecEntity, len(list))
	for i, item := range list {
		r[i] = f(item)
	}
	return r
}

/****************************************************************************************/
/*                               product entity of cmd                                  */
/****************************************************************************************/

// CreateProduct 新增商品
func (s *ProductAPIService) CreateProduct(ctx context.Context, req *productGrpc.CreateProductRequest) (res *productGrpc.CreateProductResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("productImp->CreateProduct:%v", req)
	res = &productGrpc.CreateProductResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}
	var product sqlc.Product
	var productAttributeKeys []sqlc.ProductAttributeKey
	var productAttributeValues []sqlc.ProductAttributeValue
	var productSpecs []sqlc.ProductSpec

	err = s.dao.NewTxRunner().ExecTx(ctx, func(tx *sql.Tx) (err error) {
		var attributeDefs []*model.ProductAttributeDef
		var specs []*model.ProductSpec

		for _, attrDefEntity := range req.ProductAttributeDefs {
			attributeDefs = append(attributeDefs, &model.ProductAttributeDef{
				Key:    attrDefEntity.Key,
				Values: attrDefEntity.Values,
			})
		}
		for _, specEntity := range req.ProductSpecs {
			var attributes []model.ProductAttribute
			err = json.Unmarshal([]byte(specEntity.ProductAttributes), &attributes)
			if err != nil {
				return
			}
			specs = append(specs, &model.ProductSpec{
				Name:       specEntity.Name,
				Price:      specEntity.Price,
				Attributes: attributes,
			})
		}

		product, productAttributeKeys, productAttributeValues, productSpecs, err = s.productService.CreateProduct(ctx, tx, &productService.CreateProductParam{
			SyncAction:     sqlc.EmSyncActionLCR,
			BrandID:        req.BrandId,
			Name:           req.Name,
			Price:          req.Price,
			Pics:           req.Pics,
			Intro:          req.Intro,
			IntroPics:      req.IntroPics,
			ProductGroupID: req.ProductGroupId,
			RemotePkOfProd: 0,
			AttributeDefs:  attributeDefs,
			Specs:          specs,
		})

		if err != nil {
			return
		}
		return
	})
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	res.Product = transformProductEntityToPb(productAttributeKeys, productAttributeValues, productSpecs)(product)
	return
}

// UpdateProduct 更新商品
func (s *ProductAPIService) UpdateProduct(ctx context.Context, req *productGrpc.UpdateProductRequest) (res *productGrpc.UpdateProductResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("productImp->UpdateProduct:%v", req)
	res = &productGrpc.UpdateProductResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	var product sqlc.Product
	var productAttributeKeys []sqlc.ProductAttributeKey
	var productAttributeValues []sqlc.ProductAttributeValue
	var productSpecs []sqlc.ProductSpec

	err = s.dao.NewTxRunner().ExecTx(ctx, func(tx *sql.Tx) (err error) {
		var attributeDefs []*model.ProductAttributeDef
		var specs []*model.ProductSpec

		for _, attrDefEntity := range req.ProductAttributeDefs {
			attributeDefs = append(attributeDefs, &model.ProductAttributeDef{
				Key:    attrDefEntity.Key,
				Values: attrDefEntity.Values,
			})
		}
		for _, specEntity := range req.ProductSpecs {
			var attributes []model.ProductAttribute
			err = json.Unmarshal([]byte(specEntity.ProductAttributes), &attributes)
			if err != nil {
				return
			}
			specs = append(specs, &model.ProductSpec{
				Name:       specEntity.Name,
				Price:      specEntity.Price,
				Attributes: attributes,
			})
		}

		product, productAttributeKeys, productAttributeValues, productSpecs, err = s.productService.UpdateProduct(ctx, tx, &productService.UpdateProductParam{
			SyncAction:     sqlc.EmSyncActionLUR,
			ProductID:      req.Id,
			BrandID:        req.BrandId,
			Name:           req.Name,
			Price:          req.Price,
			Pics:           req.Pics,
			Intro:          req.Intro,
			IntroPics:      req.IntroPics,
			ProductGroupID: req.ProductGroupId,
			AttributeDefs:  attributeDefs,
			Specs:          specs,
		})

		if err != nil {
			return
		}
		return
	})

	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	res.Product = transformProductEntityToPb(productAttributeKeys, productAttributeValues, productSpecs)(product)
	return
}

// RemoveProduct 删除商品
func (s *ProductAPIService) RemoveProduct(ctx context.Context, req *productGrpc.RemoveProductRequest) (res *productGrpc.RemoveProductResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("productImp->RemoveProduct:%v", req)

	res = &productGrpc.RemoveProductResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	var product sqlc.Product
	var productAttributeKeys []sqlc.ProductAttributeKey
	var productAttributeValues []sqlc.ProductAttributeValue
	var productSpecs []sqlc.ProductSpec

	err = s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {

		productID := req.Id
		productCountInActivity, err := q.CountNormalActivityProductsByBrandIDAndProductIDs(ctx, productID)
		if err != nil {
			return
		}
		if productCountInActivity > 0 {
			err = fmt.Errorf("该商品已在活动中使用,无法删除")
			return
		}

		productCountInOrder, err := q.CountOrderDetailsByBrandIDAndProductIDs(ctx, productID)
		if err != nil {
			return
		}
		if productCountInOrder > 0 {
			err = fmt.Errorf("该商品已产生订单,无法删除")
			return
		}

		productAttributeKeys, err = q.ListNormalProductAttributeKeysByProductID(ctx, productID)
		if err != nil {
			return err
		}
		slog.Info("1 获取商品属性键 ok")

		if len(productAttributeKeys) > 0 {
			disableAttributeKeysRet, err := q.DisableProductAttributeKeyByProductID(ctx, productID)
			if err != nil {
				return err
			}
			slog.Infof("2 disabled商品属性键 ok =>%v", disableAttributeKeysRet)
		} else {
			slog.Infof("2 disabled商品属性键无需删除")
		}

		productAttributeValues, err = q.ListNormalProductAttributeValuesByProductID(ctx, productID)
		if err != nil {
			return err
		}
		slog.Info("3 获取商品属性值 ok")

		if len(productAttributeKeys) > 0 {
			disableAttributeValuesRet, err := q.DisableProductAttributeValueByProductID(ctx, productID)
			if err != nil {
				return err
			}
			slog.Infof("4 disabled商品属性值 ok =>%v", disableAttributeValuesRet)
		} else {
			slog.Infof("4 disabled商品属性值无需删除")
		}

		productSpecs, err = q.ListNormalProductSpecsByProductID(ctx, productID)
		if err != nil {
			return err
		}
		slog.Info("5 获取商品规格 ok")
		if len(productAttributeKeys) > 0 {
			disableAttributeSpecsRet, err := q.DisableProductSpecByProductID(ctx, productID)
			if err != nil {
				return err
			}
			slog.Infof("6 disabled商品规格 ok =>%v", disableAttributeSpecsRet)
		} else {
			slog.Info("6 disabled商品规格无需删除")
		}

		product, err = q.DisableProduct(ctx, req.Id)
		if err != nil {
			return err
		}
		slog.Info("7 disable商品 ok")

		return
	})
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	res.Product = transformProductEntityToPb(productAttributeKeys, productAttributeValues, productSpecs)(product)
	return
}

/****************************************************************************************/
/*                            product entity of query                                   */
/****************************************************************************************/

// GetProduct 获取单个商品
func (s *ProductAPIService) GetProduct(ctx context.Context, req *productGrpc.GetProductRequest) (res *productGrpc.GetProductResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("productImp->GetProduct:%v", req)
	res = &productGrpc.GetProductResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	product, err := s.dao.Q.GetProduct(ctx, req.Id)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	productAttributeKeys, err := s.dao.Q.ListNormalProductAttributeKeysByProductID(ctx, product.ID)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	productAttributeKeyIDs := util.MapToInt64(util.ToGenericSlice(productAttributeKeys), func(v interface{}) int64 {
		productAttributeKey := v.(sqlc.ProductAttributeKey)
		return productAttributeKey.ID
	})

	productAttributeValues, err := s.dao.Q.ListNormalProductAttributeValuesByProductAttributeKeyIDs(ctx, productAttributeKeyIDs)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	productSpecs, err := s.dao.Q.ListNormalProductSpecsByProductID(ctx, product.ID)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	res.Product = transformProductEntityToPb(productAttributeKeys, productAttributeValues, productSpecs)(product)
	return
}

// ListNormalProductsByBrandID  获取正常的商品列表
func (s *ProductAPIService) ListNormalProductsByBrandID(ctx context.Context, req *productGrpc.ListNormalProductsByBrandIDRequest) (res *productGrpc.ListNormalProductsByBrandIDResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("productImp->ListNormalProductsByBrandID:%v", req)

	res = &productGrpc.ListNormalProductsByBrandIDResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	q := s.dao.Q
	ts := sqlc.PBTimestampToGoTime(req.Ts)
	brandID := req.BrandId
	productGroupID := req.ProductGroupId
	keyword := req.Keyword
	isKeywordEmpty := keyword == ""
	keywordLike := fmt.Sprintf("%%%s%%", keyword)

	total, err := q.CountNormalProductsByBrandID(ctx, sqlc.CountNormalProductsByBrandIDParams{
		Created:        ts,
		BrandID:        brandID,
		ProductGroupID: productGroupID,
		IsKeywordEmpty: isKeywordEmpty,
		Keyword:        keywordLike,
	})
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	if total == 0 {
		// 不用继续调用listfunc
		return
	}

	products, err := s.dao.Q.ListNormalProductsByBrandID(ctx, sqlc.ListNormalProductsByBrandIDParams{
		Limit:          req.Limit,
		Offset:         req.Offset,
		Created:        ts,
		BrandID:        brandID,
		ProductGroupID: productGroupID,
		IsKeywordEmpty: isKeywordEmpty,
		Keyword:        keywordLike,
	})
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	slog.Infof("products=>%v", products)
	productIDs := util.MapToInt64(util.ToGenericSlice(products), func(v interface{}) int64 {
		product := v.(sqlc.Product)
		return product.ID
	})

	productAttributeKeys, err := s.dao.Q.ListNormalProductAttributeKeysByProductIDs(ctx, productIDs)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	productAttributeValues, err := s.dao.Q.ListNormalProductAttributeValuesByProductIDs(ctx, productIDs)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	productSpecs, err := s.dao.Q.ListNormalProductSpecsByProductIDs(ctx, productIDs)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	res.Total = total
	res.Products = mapProductEntityToPb(products, transformProductEntityFilteredToPb(productAttributeKeys, productAttributeValues, productSpecs))
	return
}
